package com.breezelin.card.adapterview.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.AdapterView;
import android.widget.ListAdapter;

import com.breezelin.card.adapterview.R;
import com.breezelin.card.adapterview.model.CardModel;
import com.breezelin.card.adapterview.model.Orientations.Orientation;

import java.util.HashSet;
import java.util.Random;

public class CardContainer extends AdapterView<ListAdapter> {

	public static final int INVALID_POINTER_ID = -1;

	/**
	 * 无序排布下的最大角度偏移值
	 */
	private static final double DISORDERED_MAX_ROTATION_RADIANS = Math.PI / 64;
	/**
	 * 可视卡牌上限<br/>
	 * 可根据需求和性能限制进行设定
	 */
	private static final int MAX_VISIBLE = 4;
	/**
	 * 卡牌大小递减值
	 */
	private static final float SCALING_DECAY = 0.95f;
	/**
	 * 卡牌透明度递减值
	 */
	public static final float ALPHA_DECAY = 0.7f;
	/**
	 * 动画时间
	 */
	public static final long DURATION_ANIMATION = 300;

	/**
	 * 用于产生角度偏移的随机数
	 */
	private final Random mRandom = new Random();
	/**
	 * 整个控件区域
	 */
	private final Rect boundsRect = new Rect();
	/**
	 * 卡片区域
	 */
	private final Rect childRect = new Rect();
	/**
	 * 矩阵（暂时对布局无用）
	 */
	private final Matrix mMatrix = new Matrix();

	/**
	 * 手势识别
	 */
	private GestureDetector mGestureDetector;
	/**
	 * 上一个动作的id
	 */
	private int mActivePointerId = INVALID_POINTER_ID;
	/**
	 * 飞出动作要求的操作距离
	 */
	private int mFlingSlop;
	/**
	 * 排布方式
	 */
	private Orientation mOrientation;
	/**
	 * 适配器
	 */
	private ListAdapter mListAdapter;
	/**
	 * 上一触点的横坐标
	 */
	private float mLastTouchX;
	/**
	 * 上一触点的纵坐标
	 */
	private float mLastTouchY;
	/**
	 * 最顶端的一张卡牌
	 */
	private View mTopCard;
	/**
	 * 有效动作长度判定值
	 */
	private int mTouchSlop;
	/**
	 * 当前控件中的偏移方向
	 */
	private int mGravity;
	/**
	 * 卡片堆以外，下一张卡牌在适配器中的位置
	 */
	private int mNextAdapterPosition;
	/**
	 * 当前首张卡片是否处于滑动中
	 */
	private boolean mDragging;
	/**
	 * 当前正有卡片在飞行动画中
	 */
	private boolean mIsFlying;
	/**
	 * 飞行中的卡片集合
	 */
	private HashSet<View> mFlyingCards;

	/**
	 * 适配器的数据变化监听
	 */
	private final DataSetObserver mDataSetObserver = new DataSetObserver() {
		@Override
		public void onChanged() {

			// 一旦变更，立即进行列表的清空
			clearStack();
			// 卡片的重添加
			ensureFull();
		}

		@Override
		public void onInvalidated() {
			// 数据失效，清空卡片堆
			clearStack();
		}
	};

	public CardContainer(Context context) {
		super(context);

		// 无参数的初始化，将自动设定卡片的有序以及居中显示
		setOrientation(Orientation.Ordered);
		setGravity(Gravity.CENTER);
		init();

	}

	public CardContainer(Context context, AttributeSet attrs) {
		super(context, attrs);
		initFromXml();
		init();
	}


	public CardContainer(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initFromXml();
		init();
	}

	/**
	 * 初始化
	 */
	private void init() {

		mFlyingCards = new HashSet<>();
		// 获取需要的相关参数
		ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
		// 快速滑动的有效距离
		mFlingSlop = viewConfiguration.getScaledMinimumFlingVelocity();
		// 滑动动作有效距离
		mTouchSlop = viewConfiguration.getScaledTouchSlop();
		// 创建手势监听
		mGestureDetector = new GestureDetector(getContext(), new GestureListener());
	}

	/**
	 * 原本是从布局文件中获取属性，现在只是手动定义了属性
	 * <p/>
	 * 默认居中并且有序显示
	 */
	private void initFromXml() {
		setGravity(Gravity.CENTER);
		setOrientation(Orientation.Ordered);
	}

	@Override
	public ListAdapter getAdapter() {
		return mListAdapter;
	}

	/**
	 * 设置卡片适配器
	 *
	 * @param adapter 相应的卡片适配器
	 */
	@Override
	public void setAdapter(ListAdapter adapter) {

		// 移除之前适配器的数据监听
		if (mListAdapter != null) {
			mListAdapter.unregisterDataSetObserver(mDataSetObserver);
		}
		// 清空卡牌堆
		clearStack();
		// 重置顶部卡牌
		mTopCard = null;
		// 重设适配器
		mListAdapter = adapter;
		// 重置下一张卡牌
		mNextAdapterPosition = 0;
		// 为适配器注册数据监听
		adapter.registerDataSetObserver(mDataSetObserver);
		// 填充足够的卡片
		ensureFull();
		// 获取第一张卡片并设置其硬件渲染
		if (getChildCount() != 0) {
			mTopCard = getChildAt(getChildCount() - 1);
			mTopCard.setLayerType(LAYER_TYPE_HARDWARE, null);
		}
		// 请求进行布局
		requestLayout();
	}

	/**
	 * 填充列表的方法。<br/>
	 * 如果列表已满，则不做操作。<br/>
	 * 如果列表尚未满，则将新卡片引入卡片堆底部知道卡牌数达到限定值。
	 */
	private void ensureFull() {
		// 循环填充卡片
		while (
			// 判断条件一 适配器中有足够的卡片
				mNextAdapterPosition < mListAdapter.getCount()
						// 判断条件二 卡片的数量小于最大限制
						&& getChildCount() < MAX_VISIBLE) {

			// 得知当前已存在卡片的数量
			int childCount = getChildCount();
			// 拿出一张新的卡片，创建卡片视图
			View view = mListAdapter.getView(mNextAdapterPosition, null, this);
			// 使用软件图层，完成复杂渲染
//            view.setLayerType(LAYER_TYPE_SOFTWARE, null);
			view.setLayerType(LAYER_TYPE_HARDWARE, null);
			// 设置排布方式，默认是有序无视觉效果堆叠排列
			if (mOrientation == Orientation.Disordered) {
				// 无序堆叠排列
				view.setRotation(getDisorderedRotation());
			} else if (mOrientation == Orientation.Lined) {
				// 队列排列，带有层叠视觉效果
				// 透明度和体积递减
				view.measure(0, 0);
				int height = view.getMeasuredHeight();
				float alpha = 1.0f;
				float scale = 1.0f;
				for (int i = 0; i < childCount; i++) {
					scale *= SCALING_DECAY;
					alpha *= ALPHA_DECAY;
				}
				if (childCount == MAX_VISIBLE - 1) {
					alpha = 0.0f;
				}
				view.setAlpha(alpha);
				view.setScaleX(scale);
				view.setScaleY(scale);
				// 位置
				view.setTranslationY(height * (1 - scale) / 2 + (childCount * getResources()
						.getDimension(R.dimen.card_line_spacing)));
			}
			// 将卡片添加入卡片堆视图底部
			addViewInLayout(view, 0, // 卡片将会被插入底部
					new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
							// 传入视图类型，暂时无用
							mListAdapter.getItemViewType(mNextAdapterPosition)), false);
			// 刷新布局，使插入的视图生效
			requestLayout();

			// 更新下一张卡片编号
			mNextAdapterPosition++;
		}
	}

	/**
	 * 获取上一个飞出的卡片
	 */
	private void getPrevious() {

		// 判断是否有已经飞出的卡片
		if (mNextAdapterPosition - MAX_VISIBLE > 0) {

			View view = mListAdapter.getView(mNextAdapterPosition, null, this);

			mNextAdapterPosition--;
		}
	}

	/**
	 * 清空卡牌栈
	 */
	private void clearStack() {

		// 从布局下移除所有的视图
		removeAllViewsInLayout();
		// 重置从adapter中获取数据的指针
		mNextAdapterPosition = 0;
		// 顶端卡牌置空
		mTopCard = null;
	}

	/**
	 * @return 排布方式
	 */
	public Orientation getOrientation() {
		return mOrientation;
	}

	/**
	 * 设置排布方式
	 *
	 * @param orientation 排布方式
	 */
	public void setOrientation(Orientation orientation) {

		// 排布方式不可以为空
		if (orientation == null) {
			throw new NullPointerException("没有设定卡片排布方式");
		}
		if (mOrientation != orientation) {
			// 为排布方式赋值
			this.mOrientation = orientation;
			if (orientation == Orientation.Disordered) {

				// 散乱排布，则随机生成子控件的角度
				for (int i = 0; i < getChildCount(); i++) {
					View child = getChildAt(i);
					child.setRotation(getDisorderedRotation());
				}
			} else if (orientation == Orientation.Lined) {

				// 队列排布，将控件大小和位置进行相应的调整
				// 获取需要的参数（缩放程度以及偏移度）
				float spacing = getResources().getDimension(R.dimen.card_line_spacing);
				// 依次进行缩放以及纵向的偏移
				for (int i = 0; i < getChildCount(); i++) {
					View child = getChildAt(i);
					child.setTranslationY(spacing * i);
				}
			} else {

				// 默认排布，则不设置其倾斜程度
				for (int i = 0; i < getChildCount(); i++) {
					View child = getChildAt(i);
					child.setRotation(0);
				}
			}
			requestLayout();
		}

	}

	/**
	 * @return 随机生成的无序排布角度偏移值
	 */
	private float getDisorderedRotation() {
		return (float) Math.toDegrees(mRandom.nextGaussian() * DISORDERED_MAX_ROTATION_RADIANS);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		// todo 测定显示范围 尚不知其原理
		int requestedWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
		int requestedHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
		int childWidth, childHeight;

		// todo 根据排布方式，测知子控件大小？
		if (mOrientation == Orientation.Disordered) {
			// 随机角度的旋转，将为子控件测算更大的范围
			int R1, R2;
			if (requestedWidth >= requestedHeight) {
				R1 = requestedHeight;
				R2 = requestedWidth;
			} else {
				R1 = requestedWidth;
				R2 = requestedHeight;
			}
			// 子控件的最大测量范围，是其进行最大角度偏转以后所在的区域
			childWidth = (int) ((R1 * Math.cos(DISORDERED_MAX_ROTATION_RADIANS)
					- R2 * Math.sin(DISORDERED_MAX_ROTATION_RADIANS))
					/ Math.cos(2 * DISORDERED_MAX_ROTATION_RADIANS));
			childHeight = (int) ((R2 * Math.cos(DISORDERED_MAX_ROTATION_RADIANS)
					- R1 * Math.sin(DISORDERED_MAX_ROTATION_RADIANS))
					/ Math.cos(2 * DISORDERED_MAX_ROTATION_RADIANS));
		} else {
			// 角度非随机，不需要变更子控件测算范围
			childWidth = requestedWidth;
			childHeight = requestedHeight;
		}

		// 测算卡片的测量参数
		int childWidthMeasureSpec, childHeightMeasureSpec;
		childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST);
		childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST);

		// 依次决定子控件的测量范围
		for (int i = 0; i < getChildCount(); i++) {

			View child = getChildAt(i);
			assert child != null;

			// 依据排布方式进行子控件大小的计算
			if (mOrientation != Orientation.Lined) {
				// 非队列式排布，不需要变更测量
				child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
			} else {
				// todo 队列式排布，每次测量都比上一个小 可能要调整为缩放而不需要管测量
//                int exactWSpec = childWidthMeasureSpec;
//                int exactHSpec = childHeightMeasureSpec;
//                for (int j = 0; j < i; j++) {
//                    exactWSpec = (int) (exactWSpec * SCALING_DECAY);
//                    exactHSpec = (int) (exactHSpec * SCALING_DECAY);
				child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
//                }
			}
		}
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);

		// 循环为子控件进行布局
		for (int i = 0; i < getChildCount(); i++) {
			// 控件本身宽高
			boundsRect.set(0, 0, getWidth(), getHeight());

			// 子控件宽高
			View view = getChildAt(i);
			int w, h;
			w = view.getMeasuredWidth();
			h = view.getMeasuredHeight();

			// 为子控件设置位置
			Gravity.apply(mGravity, w, h, boundsRect, childRect);
			// 调用子控件布局方法
			view.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		// 如果顶端卡片不存在,不拦截事件
		if (mTopCard == null || mFlyingCards.size() > 0) {
			return false;
		}
		// 如果手势识别处理了事件，则拦截事件并返回
		if (mGestureDetector.onTouchEvent(event)) {
			return true;
		}
//        Log.d("Touch Event", MotionEvent.actionToString(event.getActionMasked()) + " ");

		// 初始化记录点以及移动距离
		int pointerIndex;
		float x, y;
		float dx, dy;

		// 事件追踪
		switch (event.getActionMasked()) {

			// 手指落下
			case MotionEvent.ACTION_DOWN:

				// 获取顶部卡片的区域
				mTopCard.getHitRect(childRect);

				// 获取触点
				pointerIndex = event.getActionIndex();
				x = event.getX(pointerIndex);
				y = event.getY(pointerIndex);

				if (!childRect.contains((int) x, (int) y)) {
					// 如果触点不在子控件中，则返回false
					// TODO: 2016/3/29 应当在此处触发卡片飞回事件

					// todo 获取之前飞出的卡片
					if (!mDragging && !mIsFlying) {
//                        getPrevious();
					}
					return false;
				} else {

					// 记录此触点
					mLastTouchX = x;
					mLastTouchY = y;
					mActivePointerId = event.getPointerId(pointerIndex);

					// 未知用途代码块
					float[] points = new float[]{x - mTopCard.getLeft(), y - mTopCard.getTop()};
					mTopCard.getMatrix().invert(mMatrix);
					mMatrix.mapPoints(points);

					// 设置顶部卡片倾斜
					mTopCard.setPivotX(points[0]);
					mTopCard.setPivotY(points[1]);
				}
				break;

			// 移动
			case MotionEvent.ACTION_MOVE:

				pointerIndex = event.findPointerIndex(mActivePointerId);
				if (pointerIndex == -1) {
					x = event.getX(0);
					y = event.getY(0);
				} else {
					x = event.getX(pointerIndex);
					y = event.getY(pointerIndex);
				}

				// 计算移动的距离
				dx = x - mLastTouchX;
				dy = y - mLastTouchY;
				// 移动距离有效，设置当前为拖动状态
				if (Math.abs(dx) > mTouchSlop || Math.abs(dy) > mTouchSlop) {
					mDragging = true;
				}
				// 如果并没有在拖动中，则不需要对卡片进行移动
				if (!mDragging) {
					return true;
				}

				// 拖动中
				// 使顶部卡片移动到手指相应的位置
				mTopCard.setTranslationX(mTopCard.getTranslationX() + dx);
				mTopCard.setTranslationY(mTopCard.getTranslationY() + dy);
				// 悬浮的卡片会带有一定的倾斜度
				mTopCard.setRotation(40 * mTopCard.getTranslationX() / (getWidth() / 2.f));
				// 记录当前的触点
				mLastTouchX = x;
				mLastTouchY = y;

				break;

			// 抬起或取消
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_CANCEL:
				if (!mDragging) {
					return true;
				}
				mDragging = false;
				mActivePointerId = INVALID_POINTER_ID;

				// 为控件设置飞回的动画
				switch (mOrientation) {

					// 有序卡片
					case Ordered:
						backTopCard(mTopCard, 0.0f, 0.0f, 0.0f, mTopCard.getWidth() / 2.f,
								mTopCard.getHeight() / 2.f);
						break;

					// 无序卡片
					case Disordered:
						backTopCard(mTopCard, 0.0f, 0.0f, (float) Math.toDegrees(mRandom
										.nextGaussian() * DISORDERED_MAX_ROTATION_RADIANS),
								mTopCard.getWidth() / 2.f, mTopCard.getHeight() / 2.f);
						break;

					// 其它排布方式
					default:
						backTopCard(mTopCard, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
						break;
				}
				break;

			// todo 多点离开
			case MotionEvent.ACTION_POINTER_UP:
				pointerIndex = event.getActionIndex();
				int pointerId = event.getPointerId(pointerIndex);

				if (pointerId == mActivePointerId) {
					int newPointerIndex = pointerIndex == 0 ? 1 : 0;
					mLastTouchX = event.getX(newPointerIndex);
					mLastTouchY = event.getY(newPointerIndex);

					mActivePointerId = event.getPointerId(newPointerIndex);
				}
				break;
		}

		return true;
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {

		// 顶端无卡片，不拦截事件
		if (mTopCard == null || mFlyingCards.size() > 0) {
			return false;
		}
		// 事件被手势识别截获，拦截，并且不做处理，直接返回
		if (mGestureDetector.onTouchEvent(event)) {
			return true;
		}
		// 触点编号
		final int pointerIndex;
		// 当前触点坐标
		final float x, y;
		switch (event.getActionMasked()) {

			// 手指落下
			case MotionEvent.ACTION_DOWN:
				// 顶部卡片区域赋值
				mTopCard.getHitRect(childRect);
//                CardModel cardModel = (CardModel) getAdapter().getItem(getChildCount() - 1);

				// 卡片的点击事件
//                if (cardModel.getOnClickListener() != null) {
//                    cardModel.getOnClickListener().onClick();
//                }

				// 保存触点编号
				pointerIndex = event.getActionIndex();
				// 获取触点位置
				x = event.getX(pointerIndex);
				y = event.getY(pointerIndex);

				// 如果触点不在卡片范围内，事件不拦截
				if (!childRect.contains((int) x, (int) y)) {

					// TODO: 2016/3/27 触点不在范围内，应当拦截事件用于飞回卡片
					return false;
				}

				// 记录当前触点
				mLastTouchX = x;
				mLastTouchY = y;
				mActivePointerId = event.getPointerId(pointerIndex);
				break;

			// 滑动事件
			case MotionEvent.ACTION_MOVE:

				super.onInterceptTouchEvent(event);

				// 获取事件触点
				pointerIndex = event.findPointerIndex(mActivePointerId);
				if (pointerIndex == -1) {
					// 如果事件触点不存在，则获取第一个触点
					x = event.getX(0);
					y = event.getY(0);
				} else {
					x = event.getX(pointerIndex);
					y = event.getY(pointerIndex);
				}
				// 结合上一个触点的位置判断是否构成动作
				if (Math.abs(x - mLastTouchX) > mTouchSlop
						|| Math.abs(y - mLastTouchY) > mTouchSlop) {
					// 构成动作
					float[] points = new float[]{x - mTopCard.getLeft(), y - mTopCard.getTop()};
					mTopCard.getMatrix().invert(mMatrix);
					mMatrix.mapPoints(points);
					mTopCard.setPivotX(points[0]);
					mTopCard.setPivotY(points[1]);
					// 消费时间
					return true;
				}
		}

		return false;
	}

	@Override
	public View getSelectedView() {
//        throw new UnsupportedOperationException();
		return new View(getContext());
	}

	@Override
	public void setSelection(int position) {
//        throw new UnsupportedOperationException();
	}

	/**
	 * @return 当前的卡牌显示模式
	 */
	public int getGravity() {
		return mGravity;
	}

	/**
	 * @param gravity 需要的卡牌显示模式
	 */
	public void setGravity(int gravity) {
		mGravity = gravity;
	}

	/**
	 * 自定义布局参数<br/>
	 * 加入了视图类型参数，尚无作用
	 */
	public static class LayoutParams extends ViewGroup.LayoutParams {

		/**
		 * 视图类型，无用参数
		 */
		int viewType;

		public LayoutParams(Context c, AttributeSet attrs) {
			super(c, attrs);
		}

		public LayoutParams(int width, int height) {
			super(width, height);
		}

		public LayoutParams(ViewGroup.LayoutParams source) {
			super(source);
		}

		public LayoutParams(int w, int h, int viewType) {
			super(w, h);
			this.viewType = viewType;
		}
	}

	/**
	 * 手势识别。用于识别复杂手势。
	 */
	private class GestureListener extends SimpleOnGestureListener {
		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

//            Log.d("Fling", "Fling with " + velocityX + ", " + velocityY);

			// 横向动作长度
			float dx = e2.getX() - e1.getX();
			dx = Math.abs(dx);
			// 纵向动作长度
			float dy = e2.getY() - e1.getY();
			dy = Math.abs(dy);
			// 横纵动作长度对比，较长的一个作为动作判定的标准
			dx = Math.max(dx, dy);
			// 横纵速度对比，取较大值
			float velocityX_delta = Math.max(Math.abs(velocityX), Math.abs(velocityY));
			// 判断是否要执行飞出的动画
			if (
				// 动作达到有效距离
					Math.abs(dx) > mTouchSlop
							//  &&Math.abs(velocityX) > Math.abs(velocityY)
							// 速度达到有效值
							&& Math.abs(velocityX_delta) > mFlingSlop * 3) {
				// 获取顶部卡片的位置，准备计算其目标位置
				float targetX = mTopCard.getX();
				float targetY = mTopCard.getY();
				// 初始化动画时长
				long duration = 0;

				// 圈定足够大的范围，作为卡片不可设置落点的区域
				// 即控件本身以及控件周围卡片长宽+100像素的区域
				boundsRect.set(0 - mTopCard.getWidth() - 100, 0 - mTopCard.getHeight() - 100,
						getWidth() + 100, getHeight() + 100);

				// 累加计算动画需要的时长以及卡片飞行的目标点
				while (boundsRect.contains((int) targetX, (int) targetY)) {
					// 目标点还在控件圈定区域内
					// 继续根据当前速度方向，向外扩展飞行的距离
					targetX += velocityX / 10;
					targetY += velocityY / 10;
					// 增加飞行需要的时间
					duration += 100;
				}

				// 动画长度最多500ms
				duration = Math.min(500, duration);

				// 取得当前的卡片模型，用于后续操作
				CardModel cardModel = (CardModel) getAdapter().getItem(getChildCount() - 1);

//                mTopCard = getChildAt(getChildCount() - 1);
//                if (mTopCard != null)
//                    mTopCard.setLayerType(LAYER_TYPE_HARDWARE, null);

				// 一段用于顶和踩功能的代码，如果没有设置此功能，则不需要注意
				if (cardModel.getOnCardDismissedListener() != null) {
					if (targetX > 0) {
						cardModel.getOnCardDismissedListener().onLike();
					} else {
						cardModel.getOnCardDismissedListener().onDislike();
					}
				}

				if (mOrientation == Orientation.Lined) {

					// 在队列排布的情况下，要重新排布卡片的位置
					int childCount = getChildCount();
					if (childCount > 0) {
						for (int i = 0; i < childCount - 1; i++) {
							int index = childCount - i - 2;
							View view = getChildAt(index);

							// 透明度与大小递减，位置重算
							float alpha = 1.0f;
							float scale = 1.0f;
							int height = view.getHeight();
							for (int j = 0; j < i; j++) {
								alpha *= ALPHA_DECAY;
								scale *= SCALING_DECAY;
							}
							lineUpCard(view, alpha, scale, height * (1 - scale) / 2
									+ (i * getResources().getDimension(
									R.dimen.card_line_spacing)));
						}
					}
				}

				// 卡片飞出动画
				mIsFlying = true;
				mTopCard.animate()
						// 之前累加好的时长
						.setDuration(duration)
						// 透明度变化
						.alpha(.75f)
						// 飞出的速度有渐变
						.setInterpolator(new LinearInterpolator())
						// 目标点的X
						.x(targetX)
						// 目标点的Y
						.y(targetY)
						// 带有一定的角度偏移（与水平方向的速度有关）
						.rotation(Math.copySign(45, velocityX))
						// 设置一个监听
						.setListener(new AnimatorListenerAdapter() {

							@Override
							public void onAnimationStart(Animator animation) {
								mFlyingCards.add(mTopCard);
							}

							// 动画结束以后的重要动作
							@Override
							public void onAnimationEnd(Animator animation) {
								// 移除已在视野外的第一张卡片
								removeViewInLayout(mTopCard);
								// 添加卡片至尾部
								ensureFull();
								// 将卡片的位置进行重置
								// 重设第一张卡片
								mFlyingCards.remove(mTopCard);
								mTopCard = getChildAt(getChildCount() - 1); //解决卡片不同步bug
								// 若第一张卡片存在，则将其绘制方式置为硬件
								// （因为所有卡片都使用了硬件，所以忽略此过程）
//                                if (mTopCard != null) {
//                                    mTopCard.setLayerType(LAYER_TYPE_HARDWARE, null);
//                                }
								mIsFlying = false;
							}

							// 动画取消的操作
							@Override
							public void onAnimationCancel(Animator animation) {
								// 与动画结束的操作相同
								onAnimationEnd(animation);
							}
						});

				// 动作成功完成，消费事件
				return true;
			} else {
				// 动作未成功完成，不打断事件传递
				return false;
			}
		}
	}

	/**
	 * 让卡片回归顶部
	 *
	 * @param cardView     卡片
	 * @param translationX 目标x偏移值
	 * @param translationY 目标y偏移值
	 * @param rotation     旋转度
	 * @param pivotX       x倾斜度
	 * @param pivotY       y倾斜度
	 */
	private void backTopCard(View cardView, float translationX, float translationY, float rotation,
	                         float pivotX, float pivotY) {
		ValueAnimator animator = ObjectAnimator.ofPropertyValuesHolder(cardView,
				// 飞回中心位置
				PropertyValuesHolder.ofFloat("translationX", translationX),
				PropertyValuesHolder.ofFloat("translationY", translationY),
				// 飞回时有随机的角度
				PropertyValuesHolder.ofFloat("rotation", rotation),
				// 倾斜度（事实上是0）
				PropertyValuesHolder.ofFloat("pivotX", pivotX),
				PropertyValuesHolder.ofFloat("pivotY", pivotY)
		).setDuration(DURATION_ANIMATION);

		animator.setInterpolator(new AccelerateInterpolator());
		animator.start();
	}

	/**
	 * 让单个卡片排队
	 *
	 * @param cardView          卡片
	 * @param targetAlpha       目标透明度
	 * @param targetScale       目标缩放度
	 * @param targetTranslation 目标位置偏移
	 */
	private void lineUpCard(final View cardView, float targetAlpha, float targetScale,
	                        float targetTranslation) {
		ValueAnimator animator = ObjectAnimator.ofPropertyValuesHolder(cardView,
				// 目标的位置偏移度
				PropertyValuesHolder.ofFloat("translationY", targetTranslation),
				// 目标的透明度
				PropertyValuesHolder.ofFloat("alpha", targetAlpha),
				// 目标的缩放程度
				PropertyValuesHolder.ofFloat("scaleX", targetScale),
				PropertyValuesHolder.ofFloat("scaleY", targetScale)
		).setDuration(DURATION_ANIMATION);
		animator.setInterpolator(new AccelerateInterpolator());
		animator.addListener(new AnimatorListenerAdapter() {
			@Override
			public void onAnimationStart(Animator animation) {
				mFlyingCards.add(cardView);
			}

			@Override
			public void onAnimationEnd(Animator animation) {
				mFlyingCards.remove(cardView);
			}
		});
		animator.start();
	}
}
